package com.game.battleship;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.game.battleship.adapters.BattleBoardImageAdapter;
import com.game.battleship.data.ActivityShipComunicator;
import com.game.battleship.data.FormationGenerator;
import com.game.battleship.data.Game;
import com.game.battleship.data.GameAi;
import com.game.battleship.data.Ship;
import com.game.battleship.data.ShipsStatus;
import com.game.battleship.db.DbAdapter;
import com.game.battleship.util.ShipUtil;
import com.game.battleship.views.ShipsBoardGridView;

public class BattleshipActivity extends Activity {

	public static final String GAME_TAG = "Battle Ships";
	private static final int NO_SPOT_IS_AIMED = -1;
	
	private static final int DIALOG_WINNING = 0;
	private static final int DIALOG_LOSING = 1;

	private static final int PTS_CORRECT_SHOT = 10;
	private static final int PTS_CONSEC_SHOT = 5;
	private static final int PTS_WINNING = 200;
	private static final int PTS_LOSING = 100;
	
	private Context mContext;

	private Button confirmCommand_Button;
//	private Button skipCommand_Button;
	private TextView currentScore_TextView;
	private Button surrenderCommand_Button;
	private Button fireRandomlyCommand_Button;
	private FrameLayout opponentShipsBoard_FrameLayout;
	private FrameLayout playerShipsBoard_FrameLayout;
	private ShipsBoardGridView opponentShipsBoard_GridView;
	private ShipsBoardGridView playerShipsBoard_GridView;

	private GameAi mGame;
	private BattleBoardImageAdapter mOpponentBoardAdapter;
	private BattleBoardImageAdapter mPlayerBoardAdapter;
//	private short[] mPlayerFormation;
	private Ship[] mPlayerShips;
	private int mAimedSpot = NO_SPOT_IS_AIMED;

	private View mAnimationView;
	private AnimationDrawable mAnimation;

	/**
	 * stores the shot at spots so that they cannot be shot at a second time
	 */
	protected HashMap<Integer, Integer> shotsMap = new HashMap<Integer, Integer>();

	private int mScreenWidth;
	private int mScreenHeight;
	
	private int currentScore;
	private ArrayList<Boolean> shotRecord;
	private boolean isWinner;

	private String username = "";
	private DbAdapter dbAdapter;
	
	/** Called when the activity is first created. */
	@SuppressWarnings("deprecation")
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		this.setContentView(R.layout.battleship);

		Bundle extras = this.getIntent().getExtras();
		if (extras != null && extras.containsKey(UserInfoActivity.EXT_USERNAME)) {
			username = extras.getString(UserInfoActivity.EXT_USERNAME);
			dbAdapter = new DbAdapter(this);
		}
		
		mContext = this;

		/*DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        
        mScreenHeight = dm.heightPixels;
        mScreenWidth = dm.widthPixels;*/
		mScreenWidth = this.getWindowManager().getDefaultDisplay().getWidth();
		mScreenHeight = this.getWindowManager().getDefaultDisplay().getHeight();

		initOpponentShipsBoard();

		initPlayerShipsBoard();

		currentScore = 0;
		shotRecord = new ArrayList<Boolean>();
		currentScore_TextView = (TextView)this.findViewById(R.id.currentScore_TextView);
		currentScore_TextView.setText("Score: " + currentScore);
		
		initButtons();

		initButtonsListeners();

		addBattleListeners();

		// init GameAi object
		Ship[] opponentShips = FormationGenerator.getHardcodedShipPosition();
		mGame = new GameAi(mPlayerShips, opponentShips);
		
		Toast.makeText(this, "Let's fight!", Toast.LENGTH_SHORT).show();
	}

	private void addBattleListeners() {
		Log.d(GAME_TAG, "battleListeners");

		opponentShipsBoard_GridView
				.setOnItemClickListener(new OnItemClickListener() {

					public void onItemClick(AdapterView<?> parent, View view,
							int position, long id) {
						Log.d(GAME_TAG,	"onItemClick - opponentShipsBoard - battleListeners");

						if (mAimedSpot == position) { // a 2nd click on same spot --> fire
							if (shotsMap.containsKey(mAimedSpot)) {
								Toast.makeText(mContext,
										"You've already fired this spot!",
										Toast.LENGTH_SHORT).show();
							} else {
								executeFire((ImageView) view, position);
							}
						} else {
							aimAtSpot((ImageView) view, position);
						}
					}
				});

	}

	/**
	 * Config view at aimed-spot
	 * 
	 * @param currentSpotView
	 * @param position
	 */
	private void aimAtSpot(ImageView currentSpotView, int position) {
		recoverView();
		currentSpotView.setImageResource(mOpponentBoardAdapter.getCrosshair());
		mAimedSpot = position;
	}

	/**
	 * Recover image view at previous aimed spot
	 */
	private void recoverView() {
		if (mAimedSpot != NO_SPOT_IS_AIMED) {
			ImageView oldAimedSpotView = (ImageView) mOpponentBoardAdapter.getItem(mAimedSpot);
			Integer oldImageResource = shotsMap.get(mAimedSpot);
			if (oldImageResource != null) {
				oldAimedSpotView.setImageResource(oldImageResource);
			} else {
				oldAimedSpotView.setImageResource(mOpponentBoardAdapter.getWater());
			}
		}
	}
	
	/**
	 * execute fire
	 * 
	 * @param currentSpotView image view at current attacked spot 
	 * @param position position of attacked spot on board
	 */
	private void executeFire(ImageView currentSpotView, int position) {
		short attackedSpotStatus = mGame.getSpotStatus(
				(short) Game.PLAYER_INDEX, position);
		mGame.makeShot(Game.PLAYER_INDEX, position, attackedSpotStatus);

		if (ShipsStatus.isShipAttackedStatus(attackedSpotStatus)) {
			 shotsMap.put(position, mOpponentBoardAdapter.getCrash());

			// start explode animation
			startAnimation(mAimedSpot % ShipsBoardGridView.N_COLUMNS,
					mAimedSpot / ShipsBoardGridView.N_COLUMNS,
					R.drawable.explosion);

			// set crash image at attacked-spot
			currentSpotView.setImageResource(mOpponentBoardAdapter.getCrash());
			
			this.currentScore += PTS_CORRECT_SHOT;
			this.shotRecord.add(true);
			currentScore_TextView.setText("Score: " + currentScore);
		} 
		else {
			 shotsMap.put(position, mOpponentBoardAdapter.getMiss());

			// start splash animation
			startAnimation(mAimedSpot % ShipsBoardGridView.N_COLUMNS,
					mAimedSpot / ShipsBoardGridView.N_COLUMNS,
					R.drawable.splash);

			// set miss image at attacked-spot
			currentSpotView.setImageResource(mOpponentBoardAdapter.getMiss());
			
			this.shotRecord.add(false);
		}

		if (mGame.isGameOver()) {
			isWinner = true;
			
			finishBattle();
		} else {			
			opponentAttack();
		}
		mAimedSpot = NO_SPOT_IS_AIMED;
	}

	private void finishBattle () {
		updateFinalScore(isWinner);

		if (username.compareTo("") != 0) {
			Map<String, String> user = dbAdapter.getUser(username);
			String password = user.get("password");
			int nextStage = Integer.parseInt(user.get("nextStage"));
			int score = Integer.parseInt(user.get("score")) + currentScore;
			if (isWinner) {
				nextStage++;
			}
			dbAdapter.updateUserByUsername(password, password, score, nextStage);
		}

		if (isWinner) 
			this.showDialog(DIALOG_WINNING);
		else
			this.showDialog(DIALOG_LOSING);
	}
	
	private void updateFinalScore(boolean win) {
		// TODO Auto-generated method stub
		if (win) {
//			Toast.makeText(mContext, "You win!", Toast.LENGTH_SHORT).show();
			this.currentScore += PTS_WINNING;
		}
		else {
//			Toast.makeText(mContext, "AI player win!", Toast.LENGTH_SHORT).show();
			this.currentScore -= PTS_LOSING;
		}
		Iterator<Boolean> ite = this.shotRecord.iterator();
		boolean current;
		int seq = 0;
		int nconsec = 0;
		while (ite.hasNext()) {
			current = ite.hasNext();
			if (current) {
				seq++;
				if (seq == 2) {
					nconsec += seq;
				}
				else if (seq > 2) {
					nconsec++;
				}
			}
			else {
				seq = 0;
			}
			ite.next();
		}
		this.currentScore += nconsec * PTS_CONSEC_SHOT;
//		Toast.makeText(mContext, "Final score: " + this.currentScore, Toast.LENGTH_SHORT).show();
	}

	/**
	 * Turn attacking to opponent - ai
	 */
	private void opponentAttack() {
		int position = mGame.generateShotForAi();
		short attackedSpotStatus = mGame.getSpotStatus(Game.AI_INDEX, position);
		mGame.makeShot(Game.AI_INDEX, position, attackedSpotStatus);
		
		if (ShipsStatus.isShipAttackedStatus(attackedSpotStatus)) {
			mPlayerBoardAdapter.setCrash(position);
		}
		else {
			mPlayerBoardAdapter.setMiss(position);
		}
		
		if (mGame.isGameOver()) {
			isWinner = false;
			
			finishBattle();
		}
	}

	/**
	 * Execute frame animation at given grid position (x, y)
	 * 
	 * @param x Board column index 
	 * @param y Board row index 
	 * @param resource Animation resource to play
	 */
	private void startAnimation(int x, int y, int resource) {
		Log.d(GAME_TAG, "startAnimation");

		// move animation view to cell(x, y)
		moveAnimationView(x, y);

		mAnimationView.setBackgroundResource(resource);
		mAnimation = (AnimationDrawable) mAnimationView.getBackground();

		if (mAnimation.isRunning()) {
			mAnimation.stop();
		}

		mAnimation.start();
	}

	/**
	 * Move animation to some column and row
	 * 
	 * @param x Board column index 
	 * @param y Board row index
	 */
	private void moveAnimationView(int x, int y) {
		Log.d(GAME_TAG, "moveAnimationView");

		LayoutParams layoutParams = (LayoutParams) mAnimationView.getLayoutParams();
		int viewSize = opponentShipsBoard_GridView.getColumnWidth();
		int deviation = 15;
		
		/*float dpi = getResources().getDisplayMetrics().density;
		layoutParams.width = layoutParams.height = (int) (50 * dpi);
		layoutParams.leftMargin = (int) ((x * 60 - 10) * dpi);
		layoutParams.topMargin = (int) ((y * 60 - 10) * dpi);*/
		
		layoutParams.width = layoutParams.height = viewSize * 2;
		layoutParams.leftMargin = (int) ((x * viewSize) - deviation);
		layoutParams.topMargin = (int) ((y * viewSize) - deviation);
		mAnimationView.setLayoutParams(layoutParams);
	}

	private void initPlayerShipsBoard() {
		
		ActivityShipComunicator comm = (ActivityShipComunicator) this.getIntent()
				.getBundleExtra(ArrangeShipsActivity.EXTRA_INFO)
				.getSerializable(ArrangeShipsActivity.SERIAL_INFO);
		mPlayerShips = comm.getShips();
		short[] playerFormation = ShipUtil.getFormationFromShips(mPlayerShips);

		playerShipsBoard_FrameLayout = (FrameLayout) this.findViewById(R.id.playerShipsBoard_FrameLayout);
		
		playerShipsBoard_GridView = new ShipsBoardGridView(mContext,
				mScreenWidth * 5 / 10, mScreenHeight * 3 / 10);		
		mPlayerBoardAdapter = new BattleBoardImageAdapter(mContext,
				playerShipsBoard_GridView.getColumnWidth(), playerFormation);
		playerShipsBoard_GridView.setAdapter(mPlayerBoardAdapter);

		playerShipsBoard_FrameLayout.addView(playerShipsBoard_GridView);		
	}

	private void initOpponentShipsBoard() {
		opponentShipsBoard_FrameLayout = (FrameLayout) this
				.findViewById(R.id.opponentShipsBoard_FrameLayout);

		// initialize Battle-Board-GridView				
/*		opponentShipsBoard_GridView = new ShipsBoardGridView(mContext,
				mScreenWidth, mScreenHeight);*/
//		int cellSize = (int) (30.0f * mContext.getResources().getDisplayMetrics().density);
		
//		opponentShipsBoard_GridView = (GridView) findViewById(R.id.BattleBoard);
		opponentShipsBoard_GridView = new ShipsBoardGridView(mContext,
				mScreenWidth, mScreenHeight * 7 / 10);
		mOpponentBoardAdapter = new BattleBoardImageAdapter(mContext, 
				opponentShipsBoard_GridView.getColumnWidth());
		opponentShipsBoard_GridView.setAdapter(mOpponentBoardAdapter);

		// initialize Animation-View
		mAnimationView = new View(mContext);

		opponentShipsBoard_FrameLayout.addView(opponentShipsBoard_GridView);
		opponentShipsBoard_FrameLayout.addView(mAnimationView);
	}

	private void initButtonsListeners() {
		confirmCommand_Button.setOnClickListener(new OnClickListener() {

			public void onClick(View view) {				
				if (mAimedSpot == NO_SPOT_IS_AIMED) {
					Toast.makeText(mContext, "No target to fire!", Toast.LENGTH_SHORT).show();
				}
				else {
					if (shotsMap.containsKey(mAimedSpot)) {
						Toast.makeText(mContext,
								"You've already fired this spot!",
								Toast.LENGTH_SHORT).show();
					}
					else {
						ImageView currentSpotView = (ImageView) mOpponentBoardAdapter.getItem(mAimedSpot);
						executeFire(currentSpotView, mAimedSpot);
					}
				}
			}

		});

//		skipCommand_Button.setOnClickListener(new OnClickListener() {
//
//			public void onClick(View v) {
//				opponentAttack();
//			}
//
//		});

		surrenderCommand_Button.setOnClickListener(new OnClickListener() {

			public void onClick(View v) {
				Toast.makeText(mContext, "AI player win!", Toast.LENGTH_SHORT).show();
				isWinner = false;
				
				finishBattle();
			}

		});

		fireRandomlyCommand_Button.setOnClickListener(new OnClickListener() {

			public void onClick(View v) {
				recoverView();
				mAimedSpot = mGame.getRandomShot(Game.PLAYER_INDEX);
				ImageView currentSpotView = (ImageView) mOpponentBoardAdapter.getItem(mAimedSpot);
				executeFire(currentSpotView, mAimedSpot);
			}

		});
	}

	private void initButtons() {
		confirmCommand_Button = (Button) this.findViewById(R.id.confirmCommand_Button);
//		skipCommand_Button = (Button) this
//				.findViewById(R.id.skipCommand_Button);
		surrenderCommand_Button = (Button) this
				.findViewById(R.id.surrenderCommand_Button);
		fireRandomlyCommand_Button = (Button) this
				.findViewById(R.id.fireRandomlyCommand_Button);
	}

	@Override
	protected Dialog onCreateDialog(int id) {
		AlertDialog.Builder builder;
		StringBuilder strBuilder;		
		String message;
		
		switch (id) {
		case DIALOG_WINNING:
			strBuilder = new StringBuilder();
			strBuilder.append("Congratulations - You are victorious!\n");
			strBuilder.append("You earned " + currentScore + " pts.\n");
			message = strBuilder.toString();
			
			builder = new AlertDialog.Builder(this);
			builder.setMessage(message)
			       .setCancelable(false)
			       .setNeutralButton("OK", new DialogInterface.OnClickListener() {

						public void onClick(DialogInterface dialog, int which) {
							// TODO Auto-generated method stub
							finish();
						}
			       });
			return builder.create();

		case DIALOG_LOSING:
			strBuilder = new StringBuilder();
			strBuilder.append("Sorry - You are defeated!\n");
			strBuilder.append("You earned " + currentScore + " pts.\n");
			message = strBuilder.toString();
			
			builder = new AlertDialog.Builder(this);
			builder.setMessage(message)
			       .setCancelable(false)
			       .setNeutralButton("OK", new DialogInterface.OnClickListener() {

					public void onClick(DialogInterface dialog, int which) {
						// TODO Auto-generated method stub
						finish();
					}
			       	
			       });
			return builder.create();
		}
		return null;
	}
	
	@Override
	protected void onPrepareDialog(int id, Dialog dialog) {
	    super.onPrepareDialog(id, dialog);
	    switch (id) {
	    case DIALOG_WINNING:
	        // Static dialog contents. No initialization needed
	        break;
	    case DIALOG_LOSING:
	        // Static dialog contents. No initialization needed
	        break;
	    }
	    return;
	}
}